﻿using ShareHelper;
using ShareHelper.HttpHelper;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace 小说爬取
{
    public abstract class ChapterParse : IBeginParse, IChapterParse
    {
        #region 构造函数

        /// <summary>
        /// 小说爬取
        /// </summary>
        /// <param name="uri">小说地址</param>
        public ChapterParse(Uri uri, string filtration = null)
        {
            Uri = uri;
            Chapters = new List<Chapter>();
            if (!string.IsNullOrEmpty(filtration))
                Replace = new Regex(filtration, RegexOptions.Compiled);
        }

        #endregion 构造函数

        #region 属性

        /// <summary>
        /// 小说地址
        /// </summary>
        public Uri Uri { get; }

        /// <summary>
        /// 小说名称
        /// </summary>
        public abstract string Name { get; }

        /// <summary>
        /// 章节列表
        /// </summary>
        protected List<Chapter> Chapters { get; set; }

        /// <summary>
        /// 小说章节总数量
        /// </summary>

        public int Count
        {
            get
            {
                return Chapters.Count;
            }
        }

        /// <summary>
        /// 正在下载章节数量
        /// </summary>
        public int UnderwayCount
        {
            get
            {
                return Chapters.Count(a => a.Download == DownloadState.Underway);
            }
        }

        /// <summary>
        /// 下载完成章节数量
        /// </summary>
        public int PerformCount
        {
            get
            {
                return Chapters.Count(a => a.Download == DownloadState.Perform);
            }
        }

        /// <summary>
        /// 没有下载数量
        /// </summary>
        public int BeforeCount
        {
            get
            {
                return Chapters.Count(a => a.Download == DownloadState.Before);
            }
        }

        #endregion 属性

        #region 事件

        /// <summary>
        /// 回调事件
        /// </summary>

        public event EventHandler<MessageEventArgs> Message;

        #endregion 事件

        #region 已实现接口

        /// <summary>
        /// 异步小说章节目录解析
        /// </summary>
        /// <param name="action"></param>
        public void BeginParse()
        {
            Task.Run(() =>
            {
                var data = new MessageEventArgs
                {
                    Type = MessageType.Parse,
                    State = Parse(out string err)
                };
                data.Message = data.State ? $"小说:{Name},解析成功." : err;
                Message?.Invoke(this, data);
            });
        }

        /// <summary>
        /// 异步下载小说
        /// </summary>
        /// <param name="num"></param>
        public void BeginDownload(int num)
        {
            Task.Run(() =>
            {
                var data = new MessageEventArgs
                {
                    Type = MessageType.Download,
                    State = Download(num, out string err)
                };
                data.Message = data.State ? $"小说:{Name},下载完毕." : err;
                Message?.Invoke(this, data);
            });
        }

        /// <summary>
        /// 下载小说内容
        /// </summary>
        /// <param name="num">并发线程数</param>
        /// <param name="err">错误原因</param>
        /// <returns></returns>
        public bool Download(int num, out string err)
        {
            err = null;
            if (BeforeCount > 0)
            {
                Task<string>[] Ts = new Task<string>[num];
                for (int i = 0; i < num; i++)
                {
                    NexChapter(out Ts[i]);
                }
                while (BeforeCount > 0)
                {
                    var index = Task.WaitAny(Ts);
                    if (IsSucceed(Ts[index], out err))
                        NexChapter(out Ts[index]);
                    else
                        break;
                }
            }
            else
                err = "没有需要下载的章节";
            return string.IsNullOrEmpty(err);
        }

        /// <summary>
        /// 写出小说章节
        /// </summary>
        /// <param name="path"></param>
        /// <param name="err"></param>
        /// <returns></returns>
        public bool Write(string path, out string err)
        {
            if (BeforeCount > 0)
            {
                err = "没有下载完成";
                return false;
            }
            try
            {
                err = null;
                var data = Chapters.OrderBy(a => a.Id).Select(b => Filtration(b));
                File.WriteAllLines(path, data);
                return true;
            }
            catch (Exception e)
            {
                err = e.GetErr();
                return false;
            }
        }

        private Regex Replace { get; }

        /// <summary>
        /// 生成章节内容 过滤广告
        /// </summary>
        /// <param name="chapter"></param>
        /// <returns></returns>
        private string Filtration(Chapter chapter)
        {
            var str = new StringBuilder(chapter.Name);
            str.Append("\r\n");
            if (Replace != null)
                str.Append(Replace.Replace(chapter.Text, string.Empty));
            else
                str.Append(chapter.Text);
            return str.ToString();
        }

        /// <summary>
        /// 异步写出小说
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public void BeginWrite(string path)
        {
            Task.Run(() =>
            {
                string err;
                var data = new MessageEventArgs
                {
                    Type = MessageType.Write,
                    State = Write(path, out err)
                };
                data.Message = data.State ? $"小说:{Name},写出成功." : err;
                Message?.Invoke(this, data);
            });
        }

        #endregion 已实现接口

        #region 未实现接口

        /// 解析章节目录
        /// </summary>
        /// <param name="err"></param>
        /// <returns></returns>
        public abstract bool Parse(out string err);

        /// <summary>
        /// 下载小说章节
        /// </summary>
        /// <param name="chapter"></param>
        /// <returns></returns>
        public abstract string GetChapter(Chapter chapter);

        #endregion 未实现接口

        #region 封装函数

        /// <summary>
        /// <summary>
        /// 判断是否爬取成功
        /// </summary>
        /// <param name="task"></param>
        /// <param name="eer"></param>
        /// <returns></returns>
        private bool IsSucceed(Task<string> task, out string eer)
        {
            eer = null;
            Chapter chapter = (Chapter)task.AsyncState;
            var Result = task.Result;
            //异常 或 失败
            if (Result.IsNullOrEmpty())
            {
                chapter.Download = DownloadState.Perform;
                var data = new MessageEventArgs
                {
                    Type = MessageType.Chapter,
                    State = true
                };
                data.Message = $"{chapter.Name},下载成功.";
                Message?.Invoke(this, data);
            }
            else
            {
                //获取成功打上成功标志
                //获取失败 设置成未获取的状态
                chapter.Download = DownloadState.Before;
                chapter.Retry++;
                if (chapter.Retry >= 3)//失败三次 放弃治疗
                {
                    eer = $"{chapter}\r\n{Result}";
                }
            }
            task.Dispose();
            return eer.IsNullOrEmpty();
        }

        /// <summary>
        /// 执行下一个下载任务
        /// </summary>
        /// <param name="task"></param>
        private void NexChapter(out Task<string> task)
        {
            task = null;
            var chapter = Chapters.Where(a => a.Download == DownloadState.Before).OrderBy(b => b.Id).FirstOrDefault();
            if (chapter != null)
            {
                chapter.Download = DownloadState.Underway;
                task = new Task<string>((x) => { return GetChapter((Chapter)x); }, chapter);
                task.Start();
            }
        }

        /// <summary>
        /// GET 请求
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="text"></param>
        /// <returns></returns>

        protected static bool Get(Uri uri, out string text, out string err)
        {
            WinHttp http = new WinHttp();
            http.Open(Method.GET, uri);
            var data = http.Send();
            if (data.IsSuccessStatus)
            {
                text = data.Text;
                err = string.Empty;
            }
            else
            {
                err = $"{data.Status}-{data.Message}";
                text = string.Empty;
            }
            return data.IsSuccessStatus;
        }

        #endregion 封装函数
    }
}